home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / WAIS / next-ui / DocControl.m < prev    next >
Encoding:
Text File  |  1992-06-30  |  11.6 KB  |  454 lines

  1. // DocControl.m
  2. //
  3. // Free software created 1 Feb 1992
  4. // by Paul Burchard <burchard@math.utah.edu>.
  5.  
  6. #import "DocControl.h"
  7. #import "Doc.h"
  8. #import <appkit/appkit.h>
  9. #import <sys/file.h>
  10. #import <sys/param.h>         /* for MAXPATHLEN */
  11. #import <string.h>
  12.  
  13.  
  14. @implementation DocControl
  15.  
  16. - (int)computeTypeIndexOf:(const char *)fileName inList:(const char **)theTypes
  17. {
  18.     int hNum;
  19.     const char **aType, *thisType;
  20.  
  21.     if(!fileName || !theTypes) return (-1);
  22.     if(!(thisType = strrchr(fileName, '/'))) thisType = fileName;
  23.     if(thisType = strrchr(thisType, '.')) thisType++;
  24.     else thisType = "";
  25.     for(hNum=0, aType=theTypes; *aType; hNum++, aType++)
  26.     if(0 == strcmp(*aType, thisType)) break;
  27.     if(!*aType) return (-1);
  28.     return hNum;
  29. }
  30.  
  31. - (int)getTypeAndOpenPath:(char *)fileBuf
  32.     defaultFolder:(const char *)theFolder types:(const char **)theTypes
  33. {
  34.     int hNum;
  35.     const char *const *files;
  36.     static id openPanel = nil;
  37.  
  38.     if(!fileBuf || !theTypes) return (-1);
  39.     if(!openPanel) openPanel = [OpenPanel new];
  40.     [NXApp setAutoupdate:NO];
  41.     if(theFolder && *theFolder)
  42.     {
  43.     if(![openPanel runModalForDirectory:theFolder
  44.         file:NULL types:theTypes])
  45.         { [NXApp setAutoupdate:YES]; return (-1); }
  46.     }
  47.     else
  48.     {
  49.     if(![openPanel runModalForTypes:theTypes])
  50.         { [NXApp setAutoupdate:YES]; return (-1); }
  51.     }
  52.     files = [openPanel filenames];
  53.     if(!files || !files[0])
  54.     { [NXApp setAutoupdate:YES]; return (-1); }
  55.     strcpy(fileBuf, files[0]);
  56.     [NXApp setAutoupdate:YES];
  57.     if((hNum=[self computeTypeIndexOf:fileBuf inList:theTypes]) < 0)
  58.     return (-1);
  59.     return hNum;
  60. }
  61.  
  62. // defaultPath: overrides defaultFolder: arg
  63. - (BOOL)getSavePath:(char *)fileBuf
  64.     defaultFolder:(const char *)theFolder defaultPath:(const char *)origName
  65.     type:(const char *)theType
  66. {
  67.     static id savePanel = nil;
  68.     BOOL ok, as;
  69.     char dirName[MAXPATHLEN+1], fileName[MAXPATHLEN+1];
  70.  
  71.     if(!savePanel)
  72.     {
  73.         savePanel = [SavePanel new];
  74.     [savePanel setDirectory:[[self class] defaultFolder]];
  75.     }
  76.     if(theType && *theType) [savePanel setRequiredFileType:theType];
  77.     as = NO;
  78.     if(origName && *origName)
  79.     {
  80.     char *p;
  81.     as = YES;
  82.     strcpy(dirName, origName);
  83.     if(!(p=strrchr(dirName, '/'))) as = NO;
  84.     else if(p!=dirName) *p = 0;
  85.     if(p = strrchr(origName, '/')) strcpy(fileName, p+1);
  86.     else strcpy(fileName, origName);
  87.     if(!fileName[0]) as = NO;
  88.     }
  89.     if(!as && theFolder && *theFolder) [savePanel setDirectory:theFolder];
  90.  
  91.     [NXApp setAutoupdate:NO];
  92.     if(as) ok = [savePanel runModalForDirectory:dirName file:fileName];
  93.     else ok = [savePanel runModal];
  94.     if(!ok) { [NXApp setAutoupdate:YES]; return NO; }
  95.     strcpy(fileBuf,[savePanel filename]);
  96.     [NXApp setAutoupdate:YES];
  97.     return YES;
  98. }
  99.  
  100. - init
  101. {
  102.     [super init];
  103.     convertWindowToDoc = [[HashTable alloc] initKeyDesc:"@" valueDesc:"@"];
  104.     return self;
  105. }
  106.  
  107. - setDocHandlers:HandlerList
  108. {
  109.     int i, n;
  110.     
  111.     if((n=[HandlerList count]) <= 0) return nil;
  112.     if(fileTypes) NX_FREE(fileTypes);
  113.     NX_MALLOC(fileTypes, const char *, n+1);
  114.     if(!fileTypes) return nil;
  115.     for(i=0; i<n; i++) fileTypes[i] = [[HandlerList objectAt:i] fileType];
  116.     fileTypes[i] = 0;
  117.     [DocHandlers free];
  118.     DocHandlers = HandlerList;
  119.     return self;
  120. }
  121.  
  122. + (const char *)defaultFolder
  123. {
  124.     return NXHomeDirectory();
  125. }
  126.  
  127. - appDidInit:sender
  128. {
  129.     if(launchWithCreateDoc) [self createDoc:self];
  130.     return self;
  131. }
  132.  
  133. - free
  134. {
  135.     [convertWindowToDoc free];
  136.     if(fileTypes) NX_FREE(fileTypes);
  137.     [DocHandlers free];
  138.     return [super free];
  139. }
  140.  
  141. - (BOOL)appAcceptsAnotherFile:sender
  142. {
  143.     return YES;
  144. }
  145.  
  146. - (int)app:sender openFile:(const char *)fileName type:(const char *)aType
  147. {
  148.     int hNum, nHandlers = [DocHandlers count];
  149.     
  150.     if(!fileName || !aType || [DocHandlers count]<=0) return NO;
  151.     launchWithCreateDoc = NO;
  152.     for(hNum=0; hNum<nHandlers; hNum++)
  153.         if(0 == strcmp(aType, [[DocHandlers objectAt:hNum] fileType]))
  154.         break;
  155.     if(hNum >= nHandlers) return NO;
  156.     if([self openForHandlerAt:hNum name:fileName]) return YES;
  157.     else return NO;
  158. }
  159.  
  160. - setMainDoc:aDoc
  161. {
  162.     [[aDoc window] makeKeyAndOrderFront:nil];
  163.     return self;
  164. }
  165.  
  166. - mainDoc
  167. {
  168.     if(![NXApp mainWindow]) return nil;
  169.     return (id)[convertWindowToDoc valueForKey:(void *)[NXApp mainWindow]];
  170. }
  171.  
  172. - stringTable
  173. {
  174.     return stringTable;    
  175. }
  176.  
  177. - createDocForHandlerAt:(int)hNum
  178. {
  179.     int useNum;
  180.     id theDoc;
  181.     
  182.     if([DocHandlers count] <= 0) return nil;
  183.     if(hNum<0 || hNum>=[DocHandlers count]) useNum = 0;
  184.     else useNum = hNum;
  185.     theDoc = [[[DocHandlers objectAt:useNum] alloc] init];
  186.     if(!theDoc || ![theDoc window]) { [theDoc free]; return nil; }
  187.     [self setMainDoc:theDoc];
  188.     [convertWindowToDoc
  189.         insertKey:(void *)[theDoc window] value:(void *)theDoc];
  190.     [[theDoc window] setDocEdited:NO];
  191.     return theDoc;
  192. }
  193.  
  194. - createDoc:sender
  195. {
  196.     return [self createDocForHandlerAt:(-1)];
  197. }
  198.  
  199. - createDoc1:sender
  200. {
  201.     return [self createDocForHandlerAt:0];
  202. }
  203.  
  204. - createDoc2:sender
  205. {
  206.     return [self createDocForHandlerAt:1];
  207. }
  208.  
  209. - createDoc3:sender
  210. {
  211.     return [self createDocForHandlerAt:2];
  212. }
  213.  
  214. - handlerForFile:(const char *)fileName
  215. {
  216.     int hNum;
  217.     
  218.     if(!fileName  || [DocHandlers count]<=0 || !fileTypes) return nil;
  219.     hNum = [self computeTypeIndexOf:fileName inList:fileTypes];
  220.     if(hNum<0 || hNum>=[DocHandlers count]) return nil;
  221.     return [DocHandlers objectAt:hNum];    
  222. }
  223.  
  224. - openForHandlerAt:(int)hNum name:(const char *)fileName
  225. {
  226.     int gotNum = (-1);
  227.     id theDoc, TheHandler;
  228.     char fileBuf[MAXPATHLEN+1];
  229.     const char *oneType[2] = {0, 0};
  230.  
  231.     if([DocHandlers count]<=0 || !fileTypes) return nil;
  232.     
  233.     // Get Doc handler and file name (putting latter into fileBuf).
  234.     if(fileName)
  235.     {
  236.     strcpy(fileBuf, fileName);
  237.         if(hNum>=0 && hNum<[DocHandlers count]) gotNum = hNum;
  238.     else gotNum = [self computeTypeIndexOf:fileName inList:fileTypes];
  239.     }
  240.     else if(hNum>=0 && hNum<[DocHandlers count])
  241.     {
  242.     oneType[0] = fileTypes[hNum];
  243.     gotNum = [self getTypeAndOpenPath:fileBuf
  244.         defaultFolder:[[DocHandlers objectAt:hNum] defaultFolder] 
  245.         types:oneType];
  246.     }
  247.     else gotNum = [self getTypeAndOpenPath:fileBuf
  248.         defaultFolder:[[self class] defaultFolder]
  249.     types:fileTypes];
  250.     if(gotNum<0 || gotNum>=[DocHandlers count]) return nil;
  251.     TheHandler = [DocHandlers objectAt:gotNum];
  252.     
  253.     // Check if this file is already open in some Doc; if so just pop up.
  254.     if(theDoc = [TheHandler docForFileName:fileBuf])
  255.         { [self setMainDoc:theDoc]; return theDoc; }
  256.     
  257.     // Create new Doc for this file, load it in, and pop it up.
  258.     theDoc = [[TheHandler alloc] init];
  259.     [theDoc setFileName:fileBuf];
  260.     if(!theDoc || ![theDoc window] || ![theDoc load:self])
  261.     {
  262.     NXRunAlertPanel([stringTable valueForStringKey:"Open"],
  263.         [stringTable valueForStringKey:"Cannot read %s!"],
  264.         [stringTable valueForStringKey:"OK"], NULL, NULL, fileBuf);
  265.     [theDoc free]; return nil;
  266.     }
  267.     [self setMainDoc:theDoc];
  268.     [convertWindowToDoc
  269.         insertKey:(void *)[theDoc window] value:(void *)theDoc];
  270.     [[theDoc window] setDocEdited:NO];
  271.     return theDoc;
  272. }
  273.  
  274. - open:sender
  275. {
  276.     return [self openForHandlerAt:(-1) name:NULL];
  277. }
  278.  
  279. - open1:sender
  280. {
  281.     return [self openForHandlerAt:0 name:NULL];
  282. }
  283.  
  284. - open2:sender
  285. {
  286.     return [self openForHandlerAt:1 name:NULL];
  287. }
  288.  
  289. - open3:sender
  290. {
  291.     return [self openForHandlerAt:2 name:NULL];
  292. }
  293.  
  294. - saveDoc:theDoc as:(BOOL)yn
  295. {
  296.     int choice;
  297.     id otherDoc;
  298.     const char *fileName;
  299.     char fileBuf[MAXPATHLEN+1];
  300.     
  301.     // Get new file name if still untitled or "save as" is true.
  302.     if(yn || ![theDoc fileName])
  303.     {
  304.     if(![self getSavePath:fileBuf
  305.         defaultFolder:[[theDoc class] defaultFolder]
  306.         defaultPath:[theDoc fileName] 
  307.         type:[[theDoc class] fileType]])
  308.         return nil;
  309.     // Make sure new file name is not already in use in this app.
  310.     otherDoc = [[theDoc class] docForFileName:fileBuf];
  311.     if(otherDoc && otherDoc!=theDoc)
  312.     {
  313. /*!!!
  314.         choice = NXRunAlertPanel([stringTable valueForStringKey:"Save"],
  315.         [stringTable valueForStringKey:"You already have %s open in another window.\nDestroy other window?"],
  316.         [stringTable valueForStringKey:"Yes"],
  317.         [stringTable valueForStringKey:"Cancel"], NULL, fileBuf); 
  318.         if(choice != NX_ALERTDEFAULT) return nil;
  319. !!!*/
  320.         [[otherDoc window] setDocEdited:NO];
  321.         [self closeDoc:otherDoc andFree:YES];
  322.     }
  323.     [theDoc setFileName:fileBuf];
  324.     }
  325.     if(!(fileName = [theDoc fileName])) return nil;
  326.     
  327.     // If backup option on, append "~" to the existing file.
  328.     if([[theDoc class] backupOnSave] && access(fileName, F_OK)==0)
  329.     {
  330.     strcpy(fileBuf, fileName);
  331.     strcat(fileBuf, "~");
  332.     rename(fileName, fileBuf);
  333.     }
  334.     
  335.     // Write to file and alert user on error.
  336.     if(![theDoc dump:self])
  337.     {
  338.         NXRunAlertPanel([stringTable valueForStringKey:"Save"],
  339.         [stringTable valueForStringKey:"Cannot write %s!"],
  340.         [stringTable valueForStringKey:"OK"], NULL, NULL, fileName);
  341.     return nil;
  342.     }
  343.     
  344.     [[theDoc window] setDocEdited:NO];
  345.     return theDoc;
  346. }
  347.  
  348. - save:sender
  349. {
  350.     return [self saveDoc:[self mainDoc] as:NO];
  351. }
  352.  
  353. - saveAs:sender
  354. {
  355.     return [self saveDoc:[self mainDoc] as:YES];
  356. }
  357.  
  358. - revertDocToSaved:theDoc
  359. {
  360.     const char *fileName;
  361.     
  362.     // If still untitled or unedited no reversion is possible.
  363.     if([theDoc window] && ![[theDoc window] isDocEdited]) return nil;
  364.     if(!(fileName = [theDoc fileName])) return nil;
  365.  
  366.     // Ask user if reversion is really what was meant.
  367.     if(NXRunAlertPanel([stringTable valueForStringKey:"Revert"],         
  368.     [stringTable valueForStringKey:"Revert to saved version of %s?"],
  369.     [stringTable valueForStringKey:"Revert"],
  370.     [stringTable valueForStringKey:"Cancel"], NULL, fileName)
  371.     != NX_ALERTDEFAULT)
  372.         return nil;
  373.         
  374.     // Re-read file from disk.
  375.     if(![theDoc load:self])
  376.     {
  377.     NXRunAlertPanel([stringTable valueForStringKey:"Revert"],
  378.         [stringTable valueForStringKey:"Cannot read %s!"],
  379.         [stringTable valueForStringKey:"OK"], NULL, NULL, fileName);
  380.     return nil;
  381.     }
  382.     [[theDoc window] setDocEdited:NO];
  383.     return theDoc;
  384. }
  385.   
  386. - revertToSaved:sender
  387. {
  388.     return [self revertDocToSaved:[self mainDoc]];
  389. }
  390.  
  391. - closeDoc:theDoc andFree:(BOOL)yn
  392. {
  393.     int choice;
  394.     const char *fileName;
  395.     
  396.     // Untitled files OK---user may still want to save them.
  397.     fileName = [theDoc fileName];
  398.     
  399.     // Give user a chance to save if edited.
  400.     if([[theDoc window] isDocEdited])
  401.     {
  402.     choice = NXRunAlertPanel([stringTable valueForStringKey:"Close"],
  403.             [stringTable valueForStringKey:"%s has been modified.\nSave it?"],
  404.         [stringTable valueForStringKey:"Yes"],
  405.         [stringTable valueForStringKey:"No"],
  406.         [stringTable valueForStringKey:"Cancel"],
  407.         fileName ? fileName : [stringTable valueForStringKey:"This file"]); 
  408.     switch (choice)
  409.     {
  410.     case NX_ALERTALTERNATE:
  411.         break;
  412.     case NX_ALERTOTHER:
  413.         return nil;
  414.     case NX_ALERTDEFAULT:
  415.     default:
  416.         [self saveDoc:theDoc as:(fileName ? NO : YES)];
  417.         break;
  418.     }
  419.     }
  420.     [[theDoc window] setDocEdited:NO];
  421.     
  422.     // Remove Doc from index.
  423.     [convertWindowToDoc removeKey:(void *)[theDoc window]];
  424.  
  425.     // Note that doc closes its window upon freeing.
  426.     if(yn) [[theDoc window] close];//!!!
  427.     [theDoc setFileName:NULL];//!!!
  428.     //!!!if(yn) [theDoc free];
  429.     return self;
  430. }
  431.  
  432. - close:sender
  433. {
  434.     return [self closeDoc:[self mainDoc] andFree:YES];
  435. }
  436.  
  437. - appWillTerminate:sender
  438. {
  439.     const void *windowKey;
  440.     void  *docValue;
  441.     NXHashState state = [convertWindowToDoc initState];
  442.     
  443.     // Give user a chance to review unsaved documents and cancel the Quit.
  444.     while([convertWindowToDoc nextState:&state key:&windowKey value:&docValue]) 
  445.     if([(id)windowKey isDocEdited])
  446.     {
  447.         [self setMainDoc:(id)docValue];
  448.         if(![self closeDoc:(id)docValue andFree:YES]) return nil;
  449.     }
  450.     return self;
  451. }
  452.  
  453. @end
  454.